﻿namespace JoinBox.MathEx;


using System;


public partial class Sqrt
{
    // https://blog.csdn.net/luozhuang/article/details/42265549
    // Carmack在QUAKE3中使用的计算平方根的函数
    // 在3D图形编程中,经常要求平方根或平方根的倒数,
    // 例如：求向量的长度或将向量归一化。C数学函数库中的sqrt具有理想的精度,但对于3D游戏程式来说速度太慢。
    // 我们希望能够在保证足够的精度的同时,进一步提高速度。
    // Carmack在QUAKE3中使用了下面的算法,
    // 它第一次在公众场合出现的时候,几乎震住了所有的人。
    // 据说该算法其实并不是Carmack发明的,它真正的作者是Nvidia的Gary Tarolli（未经证实）。

    //  计算参数x的平方根的倒数
    // float InvSqrt (float x)
    // {
    //     float xhalf = 0.5f*x;
    //     int i = *(int*)&x;
    //     i = 0x5f3759df - (i >> 1); ///  计算第一个近似根
    //     x = *(float*)&i;
    //     x = x*(1.5f - xhalf*x*x); ///  牛顿迭代法
    //     return x;
    // }

    /// <summary>
    /// 快速平方根倒数(Release模式才会快速运算,Debug可以勾选优化代码)
    /// </summary>
    /// <param name="x"></param>
    /// <returns></returns>
    public static float InvSqrt(float x)
    {
        unsafe
        {
            float xhalf = 0.5f * x;
            int i = *(int*)&x;              // Read bits as integer.
            i = 0x5f375a86 - (i >> 1);      // Make an initial guess for Newton-Raphson approximation
            x = *(float*)&i;                // Convert bits back to float
            x *= 1.5f - xhalf * x * x; // Perform left single Newton-Raphson step.
            return x;
        }
    }

    /// <summary>
    /// 快速平方根倒数(安全操作的方法会更慢....)
    /// </summary>
    /// <param name="x"></param>
    /// <returns></returns>
    public static float InvSqrt2(float x)
    {
        // 这个算法依赖于浮点数的内部表示和字节顺序,所以是不具移植性的。
        // 如果放到Mac上跑就会挂掉。如果想具备可移植性,还是乖乖用sqrt好了。
        float xhalf = 0.5f * x;
        int i = 0x5f375a86 - (FloatToIntBits(x) >> 1); // MagicNumber:0x5f3759df,0x5f375a86
        x = IntBitsToFloat(i);
        x *= 1.5f - xhalf * x * x; // 一次牛顿迭代
        return x;
    }

    /// <summary>
    /// java  Float.intBitsToFloat C#版
    /// </summary>
    /// <param name="i"></param>
    /// <returns></returns>
    static float IntBitsToFloat(int i)
    {
        byte[] bytes = BitConverter.GetBytes(i);
        return BitConverter.ToSingle(bytes, 0);
    }

    static int FloatToIntBits(float x)
    {
        byte[] bytes = BitConverter.GetBytes(x);
        return BitConverter.ToInt32(bytes, 0);
    }
}


